#include <xpam/net/base_socket.h>
#include <xpam/net/get_last_error.h>
#include <xpam/exception.h>
#include <sstream>


namespace xpam
{
    namespace net
    {
		int infinite = INFINITE;

        base_socket::base_socket(const address_family & af, const socket_type & type, const socket_protocol & protocol)
        {
            system::wsa::startup();
            _socket = (int)::socket(af, type, protocol);
            if (_socket == INVALID_SOCKET)
                throw xpam::exception() << "::socket() return INVALID_SOCKET";
        }

        base_socket::~base_socket(void)
        {
            close();
            system::wsa::cleanup();
        }

        void base_socket::close()
        {
            ::closesocket(_socket);
            _socket = INVALID_SOCKET;
        }

        bool base_socket::select(int interval)
        {
            timeval tv;
            if (interval >= 0)
            {
                tv.tv_sec = interval / 1000;
                tv.tv_usec = interval % 1000;
            }

            fd_set fdread;
            FD_ZERO(&fdread);
            FD_SET(_socket, &fdread);

			::select(0, &fdread, 0, 0, (interval >= 0) ? &tv : 0);

            return FD_ISSET(_socket, &fdread) != 0;
        }

        bool base_socket::is_connected()
        {
            return _socket != 0 && _socket != INVALID_SOCKET;
        }

        int base_socket::get_option(const socket_option & so)
        {
            int retval;
            int err = ::setsockopt(_socket, SOL_SOCKET, so, (char*)&retval, sizeof(retval));
            if (err != 0)
                throw xpam::exception() << "::setsockopt() returns " << err << " " << net::get_last_error();
            return retval;
        }

        void base_socket::set_option(const socket_option & so, int val)
        {
            int err = ::setsockopt(_socket, SOL_SOCKET, so, (char*)&val, sizeof(val));
            if (err != 0)
                throw xpam::exception() << "::setsockopt() returns " << err << " " << net::get_last_error();
        }
    }
}